
#ifndef __LIBERTY_ENGINE_ISERIALIZER_H__
#define __LIBERTY_ENGINE_ISERIALIZER_H__

#include <Liberty3D/Liberty3D.h>

#include <unordered_map>
#include <list>
#include <string>
#include <vector>

namespace Liberty { namespace Engine {
    template <typename T> struct Serializer;

    class ISerializer {
    public:
        virtual ~ISerializer() {}
        
    public:
        virtual bool isReading() const = 0;

    public:
        virtual bool serialize(const char* name, bool&               v, bool                    dv = false,         bool mandatory = false) = 0;
        virtual bool serialize(const char* name, int&                v, int                     dv = 0,             bool mandatory = false) = 0;
        virtual bool serialize(const char* name, unsigned int&       v, unsigned int            dv = 0,             bool mandatory = false) = 0;
        virtual bool serialize(const char* name, float&              v, float                   dv = 0,             bool mandatory = false) = 0;
        virtual bool serialize(const char* name, double&             v, double                  dv = 0,             bool mandatory = false) = 0;
        virtual bool serialize(const char* name, std::string&        v, const std::string&      dv = std::string(), bool mandatory = false) = 0;

#if defined(__LP64__)
        virtual bool serialize(const char* name, long&               v, long                    dv = 0,             bool mandatory = false) = 0;
        virtual bool serialize(const char* name, unsigned long&      v, unsigned long           dv = 0,             bool mandatory = false) = 0;
#else
        virtual bool serialize(const char* name, long long&          v, long long               dv = 0,             bool mandatory = false) = 0;
        virtual bool serialize(const char* name, unsigned long long& v, unsigned long long      dv = 0,             bool mandatory = false) = 0;
#endif

    public:
        virtual bool serialize(const char* name, Vector3& v);
        virtual bool serialize(const char* name, Quaternion& v);
        virtual bool serialize(const char* name, Uuid& v, bool mandatory = false);

    public:
        template <typename T>
        bool serialize(const char* name, std::shared_ptr<T>& v) {
            if (isReading()) {
                v = std::static_pointer_cast<T>(readObject(name));
                return true;
            }
            else {
                writeObject(name, v);
                return true;
            }
        }

        template <typename T>
        bool serialize(const char* name, std::weak_ptr<T>& v) {
            if (isReading()) {
                v = std::static_pointer_cast<T>(readObject(name));
                return true;
            }
            else {
                writeObject(name, std::static_pointer_cast<T>(v.lock()));
                return true;
            }
        }

    public:
        virtual std::shared_ptr<ISerializable> readObject(const char* name) = 0;
        virtual void writeObject(const char* name, const std::shared_ptr<ISerializable>&) = 0;
        
        virtual size_t beginCollection(const char* name) = 0;
        virtual void   endCollection  (const char* name) = 0;

        virtual size_t beginDictionary(const char* name) = 0;
        virtual void   endDictionary  (const char* name) = 0;

        virtual void beginStruct(const char* name, const char* type) = 0;
        virtual void endStruct  (const char* name, const char* type) = 0;

        virtual void close() = 0;

    public:
        static ISerializer* readerForFile  (const std::string& fileName,   ISerializableFactory* factory);
        static ISerializer* readerForStream(std::streambuf*    fileBuffer, ISerializableFactory* factory);

        static ISerializer* writerForFile  (const std::string& fileName);
        static ISerializer* writerForStream(std::streambuf*    fileBuffer);
    };

    template<> struct Serializer<bool> {
        bool operator() (ISerializer* serializer, const char* name, bool& v) {
            return serializer->serialize(name, v, false, false);
        }
    };

    template<> struct Serializer<int> {
        bool operator() (ISerializer* serializer, const char* name, int& v) {
            return serializer->serialize(name, v, 0, false);
        }
    };

    template<> struct Serializer<unsigned int> {
        bool operator() (ISerializer* serializer, const char* name, unsigned int& v) {
            return serializer->serialize(name, v, 0, false);
        }
    };

    template<> struct Serializer<float> {
        bool operator() (ISerializer* serializer, const char* name, float& v) {
            return serializer->serialize(name, v, 0.0f, false);
        }
    };

    template<> struct Serializer<double> {
        bool operator() (ISerializer* serializer, const char* name, double& v) {
            return serializer->serialize(name, v, 0.0, false);
        }
    };

    template<> struct Serializer<std::string> {
        bool operator() (ISerializer* serializer, const char* name, std::string& v) {
            return serializer->serialize(name, v, std::string(), false);
        }
    };

#if defined(__LP64__)
    template<> struct Serializer<long> {
        bool operator() (ISerializer* serializer, const char* name, long& v) {
            return serializer->serialize(name, v, 0, false);
        }
    };

    template<> struct Serializer<unsigned long> {
        bool operator() (ISerializer* serializer, const char* name, unsigned long& v) {
            return serializer->serialize(name, v, 0, false);
        }
    };
#else
    template<> struct Serializer<long long> {
        bool operator() (ISerializer* serializer, const char* name, long long& v) {
            return serializer->serialize(name, v, 0, false);
        }
    };

    template<> struct Serializer<unsigned long long> {
        bool operator() (ISerializer* serializer, const char* name, unsigned long long& v) {
            return serializer->serialize(name, v, 0, false);
        }
    };
#endif

    template<> struct Serializer<Quaternion> {
        bool operator() (ISerializer* serializer, const char* name, Quaternion& v) {
            return serializer->serialize(name, v);
        }
    };

    template<> struct Serializer<Vector3> {
        bool operator() (ISerializer* serializer, const char* name, Vector3& v) {
            return serializer->serialize(name, v);
        }
    };

    template<> struct Serializer<Uuid> {
        bool operator() (ISerializer* serializer, const char* name, Uuid& v) {
            return serializer->serialize(name, v);
        }
    };
    
    template <
        class Key,                    // unordered_map::key_type
        class T,                      // unordered_map::mapped_type
        class Hash,                   // unordered_map::hasher
        class Pred,                   // unordered_map::key_equal
        class Alloc>

    struct Serializer<std::unordered_map<Key, T, Hash, Pred, Alloc>> {
        bool operator() (ISerializer* serializer, const char* name, std::unordered_map<Key, T, Hash, Pred, Alloc>& dict) {
            typedef typename std::unordered_map<Key, T, Hash, Pred, Alloc> DictType;
            typedef typename DictType::iterator                            DictIterator;
            typedef typename std::pair<Key, T>                             DictValue;
            typedef typename DictType::value_type                          DictValueEntry;

            size_t entryCount = serializer->beginDictionary(name);

            if (serializer->isReading()) {
                DictValue pair;
                size_t    index;

                dict.clear();

                for (index = 0; index < entryCount; index++) {
                    if (Serializer<Key>()(serializer, nullptr, pair.first)) {
                        if (Serializer<T>()(serializer, nullptr, pair.second)) {
                            dict.insert(pair);
                        }
                    }
                }
            }
            else {
                DictIterator it, end(dict.end());

                for (it = dict.begin(); it != end; ++it) {
                    DictValueEntry& pair(*it);

                    Serializer<Key>()(serializer, nullptr, *const_cast<Key*>(&(pair.first)));
                    Serializer<T>()  (serializer, nullptr, pair.second);
                }
            }

            serializer->endDictionary(name);
            return true;
        }
    };

    template <typename T, class Alloc>
    struct Serializer<std::vector<T, Alloc>> {
        bool operator() (ISerializer* serializer, const char* name, std::vector<T, Alloc>& v) {
            typedef typename std::vector<T, Alloc> VectorType;
            typedef typename VectorType::iterator  VectorIterator;
            
            size_t entryCount = serializer->beginCollection(name);

            if (serializer->isReading()) {
                T      entry;
                size_t index;

                v.clear();
                v.reserve(entryCount);

                for (index = 0; index < entryCount; index++) {
                    if (Serializer<T>()(serializer, nullptr, entry)) {
                        v.push_back(entry);
                    }
                }
            }
            else {
                VectorIterator it, end(v.end());

                for (it = v.begin(); it != end; ++it) {
                    T& entry(*it);

                    Serializer<T>()(serializer, nullptr, entry);
                }
            }

            serializer->endCollection(name);
            return true;
        }
    };

    template <typename T, class Alloc>
    struct Serializer<std::list<T, Alloc>> {
        bool operator() (ISerializer* serializer, const char* name, std::list<T, Alloc>& v) {
            typedef typename std::list<T, Alloc> ListType;
            typedef typename ListType::iterator  ListIterator;
            
            size_t entryCount = serializer->beginCollection(name);

            if (serializer->isReading()) {
                T      entry;
                size_t index;

                v.clear();

                for (index = 0; index < entryCount; index++) {
                    if (Serializer<T>()(serializer, nullptr, entry)) {
                        v.push_back(entry);
                    }
                }
            }
            else {
                ListIterator it, end(v.end());

                for (it = v.begin(); it != end; ++it) {
                    T& entry(*it);

                    Serializer<T>()(serializer, nullptr, entry);
                }
            }

            serializer->endCollection(name);
            return true;
        }
    };

    template <typename T>
    struct Serializer<std::shared_ptr<T>> {
        bool operator() (ISerializer* serializer, const char* name, std::shared_ptr<T>& v) {
            return serializer->serialize(name, v);
        }
    };

    template <typename T>
    struct Serializer<std::weak_ptr<T>> {
        bool operator() (ISerializer* serializer, const char* name, std::weak_ptr<T>& v) {
            return serializer->serialize(name, v);
        }
    };
}}

#endif
